/*
 *  Header file for the FAT File System
 *
 *  Copyright(C) 2001
 *  Camilo Alejandro Arboleda
 *
 *  The contents of this file are distributed under the GNU General
 *  Public License version 2.
 *
 *  As a special exception, when this code is included in the RTEMS
 *  operating system, linking other files with RTEMS objects including
 *  this code does not cause the resulting executable application to
 *  be covered by the GNU General Public License. This exception does
 *  not however invalidate any other reasons why the executable file might
 *  be covered by the GNU General Public License.
 */

#ifndef __FAT_h
#define __FAT_h

#ifdef __cplusplus
extern "C" {
#endif

#include <rtems.h>
#include <chain.h>

#include <sys/types.h>
#include <limits.h>
#include <rtems/libio.h>

/*
 *  File name macros
 */

#define FAT_is_valid_name_char( _ch ) ( 1 )

#define FAT_is_separator( _ch ) \
   rtems_filesystem_is_separator( _ch )

/*
 *  Data types
 */

struct FAT_jnode_tt;
struct fat_geom_tt;
struct fat_block_tt;
typedef struct FAT_jnode_tt FAT_jnode_t;
typedef struct fat_geom_tt fat_geom_t;
typedef struct fat_block_tt fat_block_t;


struct fat_block_tt {
   unsigned short *entries;
   unsigned short size;
   int offset;
   unsigned changed;
};


/*
 * FAT Geometry type
 */

struct fat_geom_tt {
   int      fd;                   /* File descriptor of device */
   unsigned root_dir_offset;      /* begining of root dir */
   unsigned fat_offset;           /* Begining of fat */
   unsigned data_offset;          /* Begining of data */
   unsigned data_size;            /* Data area size */
   unsigned first_cluster_offset; /* This is not the actual offset,
                                   * because frist cluster is named
                                   * '2' and not '0', so it's the
                                   * actual offset minus 2.
				   */
   unsigned mutex_id;
   unsigned rw_mutex_id;
   unsigned fat_entries;
   unsigned short bytes_per_sect;
   unsigned short cluster_size;
   unsigned short reserved_sect;
   unsigned short total_FATs;
   unsigned short root_dir_entries;
   unsigned short logical_sectors;
   unsigned short mdb;
   unsigned short sectors_per_fat;
   unsigned short sectors_per_track;
   unsigned short head;
   unsigned short hidden_sectors;

   unsigned char  type;

/*   unsigned short fat_cache_size; */
   fat_block_t fat_cache;
};


/*
 *  FAT file information
 *
 *  The data structure for the in-memory "memfiles" is based on classic UNIX.
 *
 *  block_ptr is a pointer to a block of FAT_MEMFILE_BYTES_PER_BLOCK in
 *  length which could be data or a table of pointers to blocks.
 *
 *  Setting FAT_MEMFILE_BYTES_PER_BLOCK to different values has a significant
 *  impact on the maximum file size supported as well as the amount of
 *  memory wasted due to internal file fragmentation.  The following
 *  is a list of maximum file sizes based on various settings
 *
 *    max_filesize with blocks of   16 is         1,328
 *    max_filesize with blocks of   32 is        18,656
 *    max_filesize with blocks of   64 is       279,488
 *    max_filesize with blocks of  128 is     4,329,344
 *    max_filesize with blocks of  256 is    68,173,568
 *    max_filesize with blocks of  512 is 1,082,195,456
 */

#define FAT_FILE_BYTES_PER_BLOCK    4096
#define FAT_FILE_BLOCK_SLOTS \
  (FAT_FILE_BYTES_PER_BLOCK / sizeof(void *))

/*
 *  Defines for the file "attribute" byte
 */

#define FAT_ATTRIB_OK_MASK        0x37
#define FAT_ATTRIB_NOT_OK_MASK    0xC8
#define FAT_ATTRIB_DIR            0x10
#define FAT_ATTRIB_LONGNAME       0x0F

/*
 *  Defines for FAT directory entries
 */

#define FAT_DIRENTRY_LENGTH       32

#define FAT_DIRENTRY_ATTRIB(entry) \
  (*((unsigned char *) (entry+11)))
#define FAT_DIRENTRY_VALID(entry) \
  ( ((*((unsigned char *) entry)) != 0) \
    && ((*((unsigned char *) entry)) != 0xE5) \
    && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) )
#define FAT_FREE_ENTRY(entry) \
  ( ((*((unsigned char *) entry)) == 0) \
   || ((*((unsigned char *) entry)) == 0xE5)  \
   || ((*((unsigned char *) entry)) == 0xFE) )
#define FAT_DIRENTRY_FIRST_CLUSTER(entry) \
  ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16))
#define FAT_DIRENTRY_FILELENGTH(entry) \
  (*((unsigned long *) (entry+28)))

#define FAT_LONGDIR_ID(entry) \
  (*((unsigned char *) (entry)))
#define FAT_LONGDIR_ALIASCHECKSUM(entry) \
  (*((unsigned char *) (entry+13)))

/*
 *  What types of FAT file systems entities there can be.
 */

#define FAT_jnode_types_t rtems_filesystem_node_types_t
#define FAT_DIRECTORY     RTEMS_FILESYSTEM_DIRECTORY
#define FAT_FILE          RTEMS_FILESYSTEM_MEMORY_FILE

#define FAT_NUMBER_OF_TYPES  (FAT_MEMORY_FILE + 1)

/*
 *  Maximum length of a "basename" of an FAT file/node.
 */

#define FAT_NAME_MAX  11
#define FAT_MAX_SIZE  1024 /* Clusters */

/*
 *  The control structure for an FAT jnode.
 */

struct FAT_jnode_tt {
   Chain_Node          chain;
   FAT_jnode_t         *next;                 /* Next node */
   FAT_jnode_t         *back;                 /* Previous node */
   unsigned char       attrib;                /* attributes */
   FAT_jnode_t         *parent;               /* Parent node */
   char                name[FAT_NAME_MAX+2];  /* "basename" */
   unsigned            st_ino;                /* inode */
   int                 dir_offset;            /* Directory entry offset */
   int                 size;
   int                 reference;             /* Uses to this node */
   int                 offset;

   unsigned char       cache[512];            /* cache: 1 cluster */
   int                 cache_offset;          /* cache offset in file (bytes) */
   int                 current_cluster;       /* current cluster */

   time_t              stat_atime;            /* Time of last access */
   time_t              stat_mtime;            /* Time of last modification */
   time_t              stat_ctime;            /* Time of last status change */
   fat_geom_t          *geometry;             /* FAT geometry */
   FAT_jnode_types_t   type;                  /* Type of this entry */
};


#define FAT_update_atime( _jnode )         \
  do {                                      \
    struct timeval tv;                      \
    gettimeofday( &tv, 0 );                 \
    _jnode->stat_atime  = (time_t) tv.tv_sec; \
  } while (0)
                
#define FAT_update_mtime( _jnode )         \
  do {                                      \
    struct timeval tv;                      \
    gettimeofday( &tv, 0 );                 \
    _jnode->stat_mtime  = (time_t) tv.tv_sec; \
  } while (0)
                
#define FAT_update_ctime( _jnode )         \
  do {                                      \
    struct timeval tv;                      \
    gettimeofday( &tv, 0 );                 \
    _jnode->stat_ctime  = (time_t) tv.tv_sec; \
  } while (0)

#define FAT_atime_mtime_update( _jnode )   \
  do {                                      \
    struct timeval tv;                      \
    gettimeofday( &tv, 0 );                 \
    _jnode->stat_mtime  = (time_t) tv.tv_sec; \
    _jnode->stat_atime  = (time_t) tv.tv_sec; \
  } while (0)

typedef struct {
  ino_t                             ino_count;
  rtems_filesystem_file_handlers_r *memfile_handlers;
  rtems_filesystem_file_handlers_r *directory_handlers;
} FAT_fs_info_t;

#define increment_and_check_linkcounts( _fs_info )                  \
  ((FAT_fs_info_t * )_fs_info)->link_counts++;                     \
  if ( ((FAT_fs_info_t * )_fs_info)->link_counts  > MAXSYMLINKS )  \
    set_errno_and_return_minus_one( ELOOP )

#define decrement_linkcounts(  _fs_info )             \
  ((FAT_fs_info_t * )_fs_info)->link_counts--;        

/*
 *  Type defination for tokens returned from FAT_get_token
 */

typedef enum {
  FAT_NO_MORE_PATH,
  FAT_CURRENT_DIR,
  FAT_UP_DIR,
  FAT_NAME,
  FAT_INVALID_TOKEN
} FAT_token_types;

/*
 *  Shared Data
 */

extern rtems_filesystem_file_handlers_r       FAT_directory_handlers;
extern rtems_filesystem_file_handlers_r       FAT_file_handlers;
extern rtems_filesystem_operations_table      FAT_ops;
extern rtems_filesystem_limits_and_options_t  FAT_LIMITS_AND_OPTIONS; 

/*
 *  Routines
 */

int FAT_initialize( rtems_filesystem_mount_table_entry_t *mt_entry );
void add_node(FAT_jnode_t *node);
void del_node(FAT_jnode_t *node);
void read_fat_block(int cluster, fat_geom_t *geom);
unsigned search_next_cluster(int cluster, fat_geom_t *geom);
int find_cluster(FAT_jnode_t *node, int offset, unsigned *cluster);
int read_block(FAT_jnode_t *node, fat_geom_t *geom, int offset);
int fat_read (FAT_jnode_t *node, char *buffer, int count);
int fat_write (FAT_jnode_t *node, const char *buffer, int count);
FAT_jnode_t * FAT_find_match_in_dir(const char *token, FAT_jnode_t *parent);
int FAT_evaluate_for_make(  const char  *path, rtems_filesystem_location_info_t *pathloc, const char **name);
int FAT_eval_path(const char *pathname, int flags, rtems_filesystem_location_info_t  *pathloc);
int FAT_dir_read(rtems_libio_t *iop, void *buffer, int count);
int FAT_dir_close(rtems_libio_t *iop);
int FAT_dir_open(rtems_libio_t *iop, const char *pathname, unsigned flag, unsigned mode);
int FAT_dir_lseek( rtems_libio_t  *iop, off_t offset, int whence );
int FAT_dir_fstat( rtems_filesystem_location_info_t *loc, struct stat  *buf );
int fat_dir_read(FAT_jnode_t *node, char *buffer, int count, FAT_jnode_t *new_node);
int FAT_file_read(rtems_libio_t *iop, void *buffer, int count);
int FAT_file_close(rtems_libio_t *iop);
int FAT_file_open(rtems_libio_t *iop, const char *pathname, unsigned flag, unsigned mode);
int FAT_file_lseek( rtems_libio_t  *iop, off_t offset, int whence );
int FAT_file_stat(rtems_filesystem_location_info_t *loc, struct stat *buf );
rtems_filesystem_node_types_t FAT_node_type( rtems_filesystem_location_info_t *pathloc );
int FAT_evaluate_for_make( const char *path, rtems_filesystem_location_info_t *pathloc, const char **name);
int FAT_freenodinfo(rtems_filesystem_location_info_t  *pathloc );
int FAT_mknod( const char *token, mode_t mode, dev_t dev, rtems_filesystem_location_info_t  *pathloc );
int FAT_rmnod( rtems_filesystem_location_info_t *pathloc);
int FAT_file_write( rtems_libio_t *iop, const void *buffer, unsigned32 count);
int FAT_file_truncate( rtems_libio_t *iop, off_t length);
int FAT_link( rtems_filesystem_location_info_t  *to_loc, rtems_filesystem_location_info_t *parent_loc, const char *token);
int find_free_entry(FAT_jnode_t *node);

#ifdef __cplusplus
}
#endif

#endif
/* end of include file */
